<?php

/**
 * @package Nina
 * @author Andrew Rose <rose.andrew@gmail.com>
 */
class Nina_Ini
{
	/**
	 * Parses a configuration item looking for %CONSTANT_NAME% and replaces it with actual constant values.
	 * @param mixed $input
	 * @return mixed
	 */
	public static function &evaluate(&$input) {
		if (is_string($input) && preg_match_all("~%([A-Za-z0-9_]+(?:\\:\\:[A-Za-z0-9_]+)?)%~s", $input, $matches)) {
			foreach ($matches[1] as $constant) {
				if (defined($constant)) {
					$input = str_replace("%" . $constant . "%", constant($constant), $input);
				}
			}
		}
		return $input;
	}

	/**
	 * Parse an ini file contents and return an array ready for the registry etc..
	 * @param string $data File contents
	 * @return array
	 */
	public static function parse($data) {
		$ret = array();
		$data = explode(PHP_EOL, trim($data));
		$prefix = FALSE;

		foreach($data as $line) {
			if(strpos(trim($line), '#')===0 || !trim($line)) continue;

			if(strpos(trim($line), '!')===0) {
				$line = substr($line, 1, strlen($line));
				$negate = TRUE;
			}
			else
			{
				$negate = FALSE;
			}

			if(strpos(trim($line), '*')===0) {
				$line = substr($line, 1, strlen($line));
				$pointer = TRUE;
			}
			else
			{
				$pointer = FALSE;
			}

			$line = trim($line);

			$parts = explode("=", $line, 2);
			$partsCount = sizeof($parts);

			if($partsCount>=3) {
				throw new Nina_Exception_Ini('Syntax Error');
			}

			/* grab value (which means everything after the first occurence of a '=' */
			if($partsCount==2) {
				$part = trim($parts[1]);

				if(defined('NINA_INI_SANE')) {
					if(strpos($part, '"')===0) {
						$value = (string)substr($part, 1, strlen($part)-2);
					}
					else if(strpos($part, '%')===0) {
						$value = $part;
					}
					else
					{
						$value = (int)$part;
					}

					$value = self::evaluate($value);
				}
				else
				{
					$value = self::evaluate($part);
				}
			}
			else
			{
				$value = TRUE;
			}

			if($negate) {
				$value = 0;
			}

			/* grab path or section */
			if($partsCount>=1) {
				$part = trim($parts[0]);
				$parts = explode('[', $part);
				$partsCount = sizeof($parts);

				/* if we have 1 parts count its a clear cut path assignment, i.e. foobar = 42 */
				if($partsCount==1) {
					$ret[($pointer?'*':'').($prefix?$prefix.'.':'').$parts[0]] = $value;
				}
				else
				{
					$assocKey = trim(substr($parts[1], 0, strlen($parts[1])-1));
					/* if $parts[0] is empty (the path) we know its a pretend section so set the prefix. */
					if(empty($parts[0])) {
						$prefix = $assocKey;
						$ret[($pointer?'*':'').$assocKey] = $value;
					}
					/* if we have a assocKey, simply [somevalue] instead of [] set it */
					else if(!empty($assocKey)) {
						$ret[($pointer?'*':'').($prefix?$prefix.'.':'').$parts[0]][$assocKey] = $value; 
					}
					else /* we have [] */
					{
						$ret[($pointer?'*':'').($prefix?$prefix.'.':'').$parts[0]][] = $value; 
					}
				}
			}
		}

		return $ret;
	}
}

/*
define('NINA_INI_SANE', TRUE);
define('FOOBAR', 42);

$data = '
test.path[test0] = " hel%FOOBAR%lo"
test.path[test1] = 42
test.path[test2] = 4%FOOBAR%2
test.foo = "hel%FOOBAR%lo"
test.the = 42
test.bar = 4%FOOBAR%2
';

print_r(Nina_Ini::parse($data));
*/
